﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Net.Sockets;
using System.Threading;

namespace ControllerClient
{
   /// <summary>
   /// 客户端类
   /// </summary>
   public class FtpClient
    {
       private const int bufferSize = 65536;
       private NetworkStream controlStream;
       private NetworkStream dataStream;
       private TcpClient client;
       private string username;
       private string password;
       private TcpClient dataClient = null;
       private bool _connect = false;
       private string newIPAddress;
       private int tempPort;
       

       public FtpClient(string username, string password)
       {
           this.username = username;
           this.password = password;
       }
       /// <summary>
       /// 创建连接客户端
       /// </summary>
       /// <param name="hostname"></param>
       public void Connect(string hostname)
       {
           try
           {
               client = new TcpClient(hostname,21);//默认用22端口通信
               controlStream = client.GetStream();
               string responseMessage;
               if (GetResponse(out responseMessage) != 220)
               {
                   throw new WebException(responseMessage);
               }
               else
               {
                   _connect = true;
                   Logon(username, password);
               }
           }
           catch(Exception ex)
           {
               throw new Exception(ex.ToString());
 
           }
           
       }
       /// <summary>
       /// 读取服务器响应指令
       /// </summary>
       /// <param name="responseMessage"></param>
       /// <returns></returns>
       public int GetResponse(out string responseMessage)
       {
           byte[] response = new byte[client.ReceiveBufferSize];
           controlStream.Read(response, 0, response.Length);
           /*Encoding ecod = Encoding.GetEncoding("gb2312");
           responseMessage = ecod.GetString(response).Replace("\0", "");*/
          responseMessage = Encoding.ASCII.GetString(response).Replace("\0", "");
           return int.Parse(responseMessage.Substring(0, 3));
       }
       /// <summary>
       /// 登录操作
       /// </summary>
       /// <param name="username">用户名</param>
       /// <param name="password">密码</param>
       private void Logon(string username, string password)
       {
           string respMessage;
           int resp = SendCommand("USER "+username,out respMessage);//获取发送命令后的返回指令
           if ((resp != 331) && (resp != 230))
               throw new UnauthorizedAccessException("Unable to login to the FTP server");
           if (resp != 230)
           {
               resp = SendCommand("PASS " + password, out respMessage);//向服务器发送PASS指令，获得权限
               if (resp != 230)
                   throw new UnauthorizedAccessException("FTP server can't authenticate username");
           }
       }
       /// <summary>
       /// 向服务发送指令
       /// </summary>
       /// <param name="command"></param>
       /// <param name="respMessage"></param>
       /// <returns></returns>
       internal int SendCommand(string command, out string respMessage)
       {
           Encoding encode = Encoding.GetEncoding("gb2312");//可传送带中文字符的命令
           byte[] request = encode.GetBytes(command + "\r\n");
           controlStream.Write(request, 0, request.Length);
           return GetResponse(out respMessage);
       }
       #region 各FTP操作命令
       /// <summary>
       /// 获取文件名列表
       /// </summary>
       /// <returns></returns>
       public string[] GetFileList(string path)
       {
           if (!_connect)
               throw new Exception("You must connect first !");
          
          /* if (dataClient == null)
               dataClient = CreateDataSocket();*/
           SetBinaryMode(false);
           SetPassiveMode();
           string respMessage;
           int resp = SendCommand("NLST " + path, out respMessage);
           if ((resp != 150) && (resp != 125) && (resp != 226))
           {
               throw new Exception(respMessage);
           }
           if (dataClient == null)
               dataClient = CreateDataSocket();
           
           NetworkStream stream = dataClient.GetStream();
           Encoding encode = Encoding.GetEncoding("gb2312");
           StreamReader reader = new StreamReader(stream,encode);
           char[] seperator={'\n'};
           string[] filelist = reader.ReadToEnd().Split(seperator);
           return filelist;
       }
       /// <summary>
       /// 改变当前目录
       /// </summary>
       /// <param name="dir"></param>
       public void SetCurrentDir(string dir)
       {
           if (!_connect)
               throw new Exception("You must connect first !");
           string respMessage;
           int resp = SendCommand("CWD " + dir, out respMessage);
           if (resp != 257)
               throw new Exception(respMessage);
 
       }
       /// <summary>
       /// 创建目录
       /// </summary>
       /// <param name="dir"></param>
       public void CreateDir(string dir)
       {
           if (!_connect)
               throw new Exception("You must connect first !");
           string respMeaage;
           int resp = SendCommand("MKD " + dir, out respMeaage);
           if (resp != 250)
               throw new Exception(respMeaage);
 
       }
       /// <summary>
       /// 更改目录名称
       /// </summary>
       /// <param name="oldDir"></param>
       /// <param name="newDir"></param>
       public void RenameDir(string oldDir, string newDir)
       {
           if (!_connect)
               throw new Exception("You must connect first !");
           string respMessage;
           int resp = SendCommand("RNFR " + oldDir, out respMessage);
           if (resp != 350)
               throw new Exception(respMessage);
           resp = SendCommand("RNTO " + newDir,out respMessage);
           if (resp != 250)
               throw new Exception(respMessage);
 
       }
       /// <summary>
       /// 删除目录
       /// </summary>
       /// <param name="dir"></param>
       public void DeleteDir(string dir)
       {
           if (!_connect)
               throw new Exception("You must connect first !");
           string respMessage;
           int resp = SendCommand("RMD " + dir, out respMessage);
           if (resp != 250)
               throw new Exception(respMessage);
 
       }
       public string GetCurrnentDir()
       {
           string currnetDir="";
           if (!_connect)
               throw new Exception("You must connect first !");
           string respMessage;
           int resp = SendCommand("PWD", out respMessage);
           if (resp != 257)
               throw new Exception(respMessage);
           else
               currnetDir = respMessage.TrimEnd("\r\n".ToCharArray()).Split(" ".ToCharArray())[1];
           return currnetDir;

       }
       /// <summary>
       /// 文件重命名
       /// </summary>
       /// <param name="fileName"></param>
       public void RenameFile(string oldfileName,string newfilename)
       {
           if (!_connect)
               throw new Exception("You must connect first!");
           string respMessage;
           int resp = SendCommand("RNFR " + oldfileName, out respMessage);
           if (resp != 350)
               throw new Exception(respMessage);
           resp = SendCommand("RNTO " + newfilename, out respMessage);
           if (resp != 250)
               throw new Exception(respMessage);

       }
       public void DeleteFile(string filename)
       {
           if (!_connect)
               throw new Exception("You must connect first!");
           string respMessage;
           int resp = SendCommand("DELE " + filename, out respMessage);
           if (resp != 250)
               throw new Exception(respMessage);

       }
       /// <summary>
       /// 上传文件到服务器
       /// </summary>
       /// <param name="filename">要上传的文件</param>
       /// <param name="path">服务器路径</param>
       public void UpLoadFile(string filename,string path)
       {
           if (!_connect)
               throw new Exception("You must connect first!");
           try
           {
               using (FileStream fs = File.OpenRead(filename))
               {
                   UpLoadFile(fs, Path.GetFileName(filename),path);
               }
           }
           catch(Exception ex)
           {
               throw new Exception(ex.ToString());

           }

 
       }
       public void UpLoadFile(Stream stream, string fileName,string path)
       {
           if (!_connect)
               throw new Exception("You must connect first!");
           SetBinaryMode(true);
           /*if (dataClient == null)
               dataClient = CreateDataSocket();*/
           SetPassiveMode();
           string newfilename = path + "\\" + fileName;
           string respMessage;
           int resp = SendCommand("STOR " + newfilename, out respMessage);
           if (!((resp == 125) || (resp == 150)))
               throw new Exception(respMessage);
           if (dataClient == null)
               dataClient = CreateDataSocket();
           int bufferLength=2048;
           byte[] buffer=new byte[bufferLength];
           int countLength=stream.Read(buffer,0,bufferLength);
           while (countLength!=0)
           {
               dataStream = dataClient.GetStream();
               dataStream.Write(buffer, 0, countLength);
               countLength=stream.Read(buffer, 0, bufferLength);

           }

       }
       /// <summary>
       /// 下载文件
       /// </summary>
       /// <param name="filename">服务器中的文件名</param>
       /// <param name="storeFileName">要保存的文件名</param>
       public void DownLoadFile(string filename, string storeFileName)
       {
           if (!_connect)
               throw new Exception("You must connect first!");
           try
           {
               using (FileStream fs = File.Create(storeFileName))
               {
                   DownLoadFile(filename, fs);

               }
           }
           catch (Exception ex)
           {
               throw new Exception(ex.ToString());
           }

 
       }
       /// <summary>
       /// 下载文件到指定的文件流
       /// </summary>
       /// <param name="filename">要下载的文件</param>
       /// <param name="stream">存入的文件流</param>
       public void DownLoadFile(string filename, Stream stream)
       {
           if (!_connect)
               throw new Exception("You must connect first!");
           SetBinaryMode(true);
           SetPassiveMode();
           
           string respMessage;
           int resp = SendCommand("RETR " + filename, out respMessage);
           if (!((resp == 125) || (resp == 150)))
               throw new Exception(respMessage);
           if (dataClient == null)
               dataClient = CreateDataSocket();
           int buffersize = 2048;
           byte[] buffer = new byte[buffersize];
           dataStream=dataClient.GetStream();
           int contentLength=dataStream.Read(buffer,0,buffersize);
           while (contentLength != 0)
           {
               stream.Write(buffer, 0, contentLength);
               contentLength = dataStream.Read(buffer, 0, buffersize);
           }
 
       }

       /// <summary>
       /// 向服务器发送指定数据发送方式指令
       /// </summary>
       /// <param name="binaryMode"></param>
       private void SetBinaryMode(bool binaryMode)
       {
           int resp;
           string respMessage;
           if (binaryMode)
               resp = SendCommand("TYPE I", out respMessage);
           else
               resp = SendCommand("TYPE A", out respMessage);
           if (resp != 200)
               throw new WebException(respMessage);
       }
       #endregion

       #region 创建连接客户端
       /// <summary>
       /// 创建连接客户端,默认使用被动模式
       /// </summary>
       /// <returns></returns>
       private TcpClient CreateDataSocket()
       {
           TcpClient dataClient = new TcpClient();
           try
           {
               IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(newIPAddress), tempPort);
               dataClient.Connect(remoteEP);
           }
           catch
           {
               throw new WebException("Can't connect to remote server");

           }
           return dataClient;

       }
       /// <summary>
       /// 设置被动模式
       /// </summary>
       private void SetPassiveMode()
       {
           string respMessage;
           int resp = SendCommand("PASV", out respMessage);
           if (resp != 227)
               throw new WebException(respMessage);
           int[] parts = new int[6];
           try
           {
               int index1 = respMessage.IndexOf('(');
               int index2 = respMessage.IndexOf(')');
               string endPointData = respMessage.Substring(index1 + 1, index2 - index1 - 1);
               string[] endPointParts = endPointData.Split(',');
               for (int i = 0; i < 6; i++)
                   parts[i] = int.Parse(endPointParts[i]);
 
           }
           catch
           {
               throw new WebException("Malformed PASV reply:" + respMessage);

           }
           newIPAddress = parts[0] + "." + parts[1] + "." + parts[2] + "." + parts[3];//IP地址
           tempPort = (parts[4] << 8) + parts[5];//新的侦听端

       }
       /// <summary>
       /// 设定新的通信端口
       /// </summary>
       /// <param name="portnumber"></param>
       private void SetPort(int portnumber)
       {
           try
           {
               LockTcpClient();
               int porthigh = portnumber >> 8;
               int portlow = portnumber & 255;
               IPHostEntry entry = Dns.GetHostEntry(Dns.GetHostName());
               string ip = entry.AddressList[0].ToString().Replace(".", ",");
               string respMessage;
               int resp = SendCommand("PORT " + ip + "," + porthigh + "," + portlow, out respMessage);
               if (resp != 200)
                   throw new Exception(respMessage);
               UnlockTcpClient();
           }
           catch(Exception ex)
           {
               throw new Exception(ex.ToString());
 
           }
 
       }
       private void LockTcpClient()
       {
           Monitor.Enter(this.dataClient);
       }
       private void UnlockTcpClient()
       {
           Monitor.Exit(this.dataClient);
       }
      
       #endregion
       /// <summary>
       /// 关闭客户端，并注销
       /// </summary>
       public void Close()
       {
           if (dataStream != null)
           {
               dataStream.Close();
               dataStream = null;
           }
           string respMessage;
           GetResponse(out respMessage);
           int resp=SendCommand("QUIT", out respMessage);
           if (resp != 221)
               throw new WebException(respMessage);
           //Logoff();
           controlStream.Close();
           client.Close();
          
       }
      
    
   }
}
